package mint.tools.CLI;

import java.io.File;
import java.util.Vector;

/**
 * A generic command-line interface for iic2322 compilers. This class provides
 * command-line parsing for student projects. It recognizes the required
 * <tt>-target</tt>, <tt>-debug</tt>, <tt>-opt</tt>, and <tt>-o</tt> switches,
 * and generates a name for input and output files.
 */

public class CLI {
	/**
	 * Target value indicating that the compiler should produce its default
	 * output.
	 */
	public static final int DEFAULT = 0;

	/**
	 * Target value indicating that the compiler should scan the input and stop.
	 */
	public static final int SCAN = 1;

	/**
	 * Target value indicating that the compiler should scan and parse its
	 * input, and stop.
	 */
	public static final int PARSE = 2;

	/**
	 * Target value indicating that the compiler should produce a high-level
	 * intermediate representation from its input, and stop. This is not one of
	 * the segment targets for Fall 2006, but you may wish to use it for your
	 * own purposes.
	 */
	public static final int INTER = 3;

	/**
	 * Target value indicating that the compiler should produce a low-level
	 * intermediate representation from its input, and stop.
	 */
	public static final int SEMANTIC = 4;

	/**
	 * Target value indicating that the compiler should produce assembly from
	 * its input.
	 */
	public static final int SYMTABLE = 5;
	
	public static final int ASSEMBLY = 6;
	
	public static String directory = "";
	
	/******************Static Variables*****************************/

	/**
	 * Array indicating which optimizations should be performed. If a particular
	 * element is true, it indicates that the optimization named in the
	 * optnames[] parameter to parse with the same index should be performed.
	 */
	private boolean opts[];

	/**
	 * Vector of String containing the command-line arguments which could not
	 * otherwise be parsed.
	 */
	private Vector<String> extras;

	/**
	 * Vector of String containing the optimizations which could not be parsed.
	 * It is okay to complain about anything in this list, even without the
	 * <tt>-debug</tt> flag.
	 */
	private Vector<String> extraopts;

	/**
	 * Name of the file to put the output in.
	 */
	private String outfile;

	/**
	 * Name of the file to get input from. This is null if the user didn't
	 * provide a file name.
	 */
	private String infile;

	/**
	 * The target stage. This should be one of the integer constants defined
	 * elsewhere in this package.
	 */
	private int target;

	/**
	 * The debug flag. This is true if <tt>-debug</tt> was passed on the command
	 * line, requesting debugging output.
	 */
	private boolean debug;

	/**
	 * Sets up default values for all of the result fields. Specifically, sets
	 * the input and output files to null, the target to DEFAULT, and the extras
	 * and extraopts arrays to new empty Vectors.
	 */
	public CLI() {
		outfile = null;
		infile = null;
		target = DEFAULT;
		extras = new Vector<String>();
		extraopts = new Vector<String>();
		debug = false;
	}
	
	public String getOutFile()
	{
		return outfile;
	}
	
	public String getInFile()
	{
		return infile;
	}
	
	public int getTarget()
	{
		return target;
	}
	
	public String getExtra(int index)
	{
		return extras.get(index);
	}
	
	public int getExtraQuantity()
	{
		return extras.size();
	}
	
	public String getExtraOpt(int index)
	{
		return extraopts.get(index);
	}
	
	public int getExtraOptQuantity()
	{
		return extraopts.size();
	}
	
	public boolean isDebug()
	{
		return debug;
	}
	
	public boolean getOpt(int i)
	{
		return opts[i];
	}
	
	public int getOptsQuantity()
	{
		return opts.length;
	}
	
	/**
	 * Parse the command-line arguments. Sets all of the result fields
	 * accordingly. <BR>
	 * 
	 * <TT>-target <I>target</I></TT> sets the CLI.target field based on the
	 * <I>target</I> specified. <BR>
	 * <TT>scan</TT> or <TT>scanner</TT> specifies CLI.SCAN <TT>parse</TT>
	 * specifies CLI.PARSE <TT>inter</TT> specifies CLI.INTER <TT>lowir</TT>
	 * specifies CLI.LOWIR <TT>assembly</TT> or <TT>codegen</TT> specifies
	 * CLI.ASSEMBLY
	 * 
	 * The boolean array opts[] indicates which, if any, of the optimizations in
	 * optnames[] should be performed; these arrays are in the same order.
	 * 
	 * @param args
	 *            Array of arguments passed in to the program's Main function.
	 * @param optnames
	 *            Ordered array of recognized optimization names.
	 */
	public void parse(String args[], String optnames[]) {
		int context = 0;		

		opts = new boolean[optnames.length];

		for (int i = 0; i < args.length; i++) {
			if (args[i].equals("-debug")) {
				context = 0;
				debug = true;
			} else if (args[i].equals("-opt"))
				context = 1;
			else if (args[i].equals("-o"))
				context = 2;
			else if (args[i].equals("-target"))
				context = 3;
			else if (context == 1) {
				processOptNames(args[i], optnames);
			} else if (context == 2) {
				outfile = args[i];
				context = 0;
			} else if (context == 3) {
				// Process case insensitive.
				String argSansCase = args[i].toLowerCase();
				
				processTarget(argSansCase);
				context = 0;
			} else
				extras.addElement(args[i]);
		}

		getInputFileName();
		getOutputFileName();
	}

	private void processOptNames(String arg, String[] optnames) {
		boolean hit = false;
		for (int j = 0; j < optnames.length; j++) {
			if (arg.equals("all") || (arg.equals(optnames[j]))) {
				hit = true;
				opts[j] = true;
			}
			if (arg.equals("-" + optnames[j])) {
				hit = true;
				opts[j] = false;
			}
		}
		if (!hit)
			extraopts.addElement(arg);
	}

	private void processTarget(String argSansCase) {
		// accept "scan" and "scanner" due to handout mistake
		if (argSansCase.equals("scan") || argSansCase.equals("scanner"))
			target = SCAN;
		else if (argSansCase.equals("parse"))
			target = PARSE;
		else if (argSansCase.equals("inter"))
			target = INTER;
		else if (argSansCase.equals("analize"))
			target = SEMANTIC;
		else if (argSansCase.equals("symtable"))
			target = SYMTABLE;
		else if (argSansCase.equals("assembly"))
			target = ASSEMBLY;
		else
			target = DEFAULT; // Anything else is just default
	}

	private void getInputFileName() {
		// grab infile and lose extra args
		int i = 0;
		while (infile == null && i < extras.size()) {
			String fn = (String) extras.elementAt(i);

			if (fn.charAt(0) != '-') {
				infile = fn;
				extras.removeElementAt(i);
			}
			i++;
		}
	}
	
	private void getOutputFileName() {
		String ext = ".out";
		
		// create outfile name
		switch (target) {
		case SCAN:
			ext = ".scan";
			break;
		case PARSE:
			ext = ".parse";
			break;
		case INTER:
			ext = ".ir";
			break;
		case SEMANTIC:
			ext = ".analize";
			break;
		case SYMTABLE:
			ext = ".symtable";
			break;
		case ASSEMBLY:
			ext = ".assembly";
			break;
		case DEFAULT:
		default:
			ext = ".out";
			break;
		}

		if (outfile == null && infile != null) {			
			int dot = infile.lastIndexOf('.');			
			int slash = infile.lastIndexOf(File.separatorChar);
			// Last dot comes after last slash means that the file
			// has an extention. Note that the base case where dot
			// or slash are -1 also work.
			if (dot <= slash)
				outfile = infile + ext;
			else
				outfile = infile.substring(0, dot) + ext;
			
			if (slash >= 0 )
			{
				directory = infile.substring(0, slash) + File.separatorChar;								
			}
			else
				directory = "";
		}
	}
}
